package com.stone.serverless.core.classloader.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.stone.serverless.core.classloader.JarModuleClassLoader;
import com.stone.serverless.sdk.ProxyClassConfig;
import com.stone.serverless.sdk.constant.ClassPathConstant;
import com.stone.serverless.sdk.domain.ServiceInfo;
import com.stone.serverless.sdk.event.ComponentConsumer;
import com.stone.serverless.sdk.event.ComponentProvider;
import com.stone.serverless.sdk.handler.ComponentVirtualInvocationHandler;
import com.stone.serverless.sdk.handler.RemoteInvokeHandler;
import com.stone.serverless.sdk.loader.JarLoadService;
import com.stone.serverless.sdk.loader.ModuleClassLoader;
import com.stone.serverless.sdk.utils.LambdaUtil;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/24 9:20
 * @Version: 1.0.0
 */
public class ResolveServiceUtil {

    /**
     * 解析依赖
     *
     */
    @SneakyThrows
    public static void resolveDependencies(ModuleClassLoader moduleClassLoader) {
        /**
         * 获取当前组件声明的需要哪些服务
         */
        List<ServiceInfo> services = moduleClassLoader.getServices();
        if (ObjectUtil.isNull(services)) {
            return;
        }
        /**
         * 获取当前组件声明的需要哪些服务
         */
        for (ServiceInfo v : services) {
            //设置服务
            setDependOutService(moduleClassLoader, v);
        }
    }


    /**
     * 设置服务
     *
     * @param moduleClassLoader
     * @param serviceInfo
     */
    @SneakyThrows
    public static void setDependOutService(ModuleClassLoader moduleClassLoader, ServiceInfo serviceInfo) {
        String componentName = serviceInfo.getComponentName();
        //如果是system服务
        String classService = serviceInfo.getClassPath();

        String version = serviceInfo.getVersion();

        localFindService(classService, componentName, version, moduleClassLoader);

        if (BooleanUtil.isTrue(serviceInfo.getRemoteCall())) {
            //找远端
            remoteFindService(moduleClassLoader, componentName, version, classService);
        }
    }

    @SneakyThrows
    private static Object remoteFindService(ModuleClassLoader moduleClassLoader, String componentName, String version, String classService) {
        //再找到远程调用RPC
        Object rpcService = findDependOutService(moduleClassLoader,
                ClassPathConstant.REMOTE_CALL_SERVICE_CLASS_PATH);
        //获得目标服务信息
        if (ObjectUtil.isNull(rpcService)) {
            return null;
        }

        Object instance = moduleClassLoader.getComponentConsumer();
        if (ObjectUtil.isNull(instance)) {
            return null;
        }

        Map<String, Object> consumerBeanMapping = ReflectUtil.invoke(instance,
                LambdaUtil.getMethodName(ComponentConsumer::getConsumerBeanMapping));

        Field field = getField(instance.getClass(), classService);
        if (ObjectUtil.isNull(field)) {
            return null;
        }

        //开始代理
        Class<?> aClass = moduleClassLoader.loadClass(RemoteInvokeHandler.class.getName());
        Object oyt = ReflectUtil.newInstance(aClass, componentName, version);
        Class<?> type = field.getType();

        Object proxy = ProxyClassConfig.proxy(type, moduleClassLoader.getClassloader(), oyt);
        field.setAccessible(true);
        field.set(instance, proxy);

        consumerBeanMapping.put(classService, proxy);
        return proxy;
    }

    public static Object findDependOutService(ModuleClassLoader moduleClassLoader, String classPath) {
        List<ServiceInfo> services = moduleClassLoader.getServices();
        if (CollUtil.isEmpty(services)) {
            return null;
        }
        ServiceInfo serviceInfo = services.stream().filter(v -> v.getClassPath().equals(classPath)).findFirst().orElse(null);
        if (ObjectUtil.isNull(serviceInfo)) {
            return null;
        }
        //找到注册中心的ClassLoader
        ModuleClassLoader jarModuleClassLoader = moduleClassLoader.getJarLoadService().getJarModuleClassLoader(serviceInfo.getComponentName(), serviceInfo.getVersion());
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            return null;
        }
        //注册中心服务
        Object outService = findProvideServiceByClass(jarModuleClassLoader, serviceInfo.getClassPath());
        return outService;
    }

    @SneakyThrows
    private static Object localFindService(String classService, String componentName, String version, ModuleClassLoader moduleClassLoader) {
        ModuleClassLoader jarModuleClassLoader = moduleClassLoader.getJarLoadService().getJarModuleClassLoader(componentName, version);
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            //如果名称是系统服务
            if (componentName.equals("system")) {
                jarModuleClassLoader = moduleClassLoader;
            }
        }
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            return null;
        }
        Class<?> targetAcl = jarModuleClassLoader.loadClass(classService);
        if (ObjectUtil.isNull(targetAcl)) {
            return null;
        }

        Object instance = moduleClassLoader.getComponentConsumer();
        if (ObjectUtil.isNull(instance)) {
            return null;
        }

        Class<?> aClass = moduleClassLoader.loadClass(ComponentVirtualInvocationHandler.class.getName());
        Object oyt = ReflectUtil.newInstance(aClass, componentName, version, classService);

        Map<String, Object> consumerBeanMapping = ReflectUtil.invoke(instance,
                LambdaUtil.getMethodName(ComponentConsumer::getConsumerBeanMapping));

        //否则put一把
        Class<?> type = null;
        Field field = getField(instance.getClass(), classService);
        if (ObjectUtil.isNotNull(field)) {
            type = field.getType();
        } else {
            type = moduleClassLoader.loadClass(classService);
        }
        if (ObjectUtil.isNull(type)) {
            return null;
        }
        Object proxy = ProxyClassConfig.proxy(type, moduleClassLoader.getClassloader(), oyt);
        if (ObjectUtil.isNotNull(field)) {
            //开始代理
            field.setAccessible(true);
            field.set(instance, proxy);
        }

        consumerBeanMapping.put(classService, proxy);
        return proxy;
    }

    public static Object getDependOutServiceByComponentInfo(String classService, String componentName,
                                                            String version, JarLoadService jarLoadService) {
        Object outService = null;
        ModuleClassLoader jarModuleClassLoader = jarLoadService.getJarModuleClassLoader(componentName, version);
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            return null;
        }
        outService = findProvideServiceByClass(jarModuleClassLoader, classService);
        return outService;
    }


    public static Field getField(Class cl, String filedType) {
        Field field = null;
        //获取这个类的所有字段
        Field[] fields = ReflectUtil.getFields(cl);
        for (int i = 0; i < fields.length; i++) {
            Field tpField = fields[i];
            if (tpField.getType().getName().equals(filedType)) {
                field = tpField;
                break;
            }
        }
        return field;
    }


    @SneakyThrows
    public static Object findProvideServiceByClass(ModuleClassLoader moduleClassLoader, String aClass) {
        //声明的服务提供者
        Object exposeService = moduleClassLoader.getComponentProvider();
        if (ObjectUtil.isNull(exposeService)) {
            return null;
        }
        /**
         * 所有提供的服务
         */
        List res = ReflectUtil.invoke(exposeService,
                LambdaUtil.getMethodName(ComponentProvider::getProvider));
        //获得向外输出的类
        Object outService = null;
        if (null != res && res.size() > 0) {
            //循环获取类信息
            for (Object service : res) {
                //如果当前类名匹配直接返回
                Class<?> targetClass = moduleClassLoader.loadClass(aClass);
                if (ClassUtil.isAssignable(targetClass, service.getClass())) {
                    outService = service;
                    break;
                }
            }
        }
        return outService;
    }

    public static void uninstallDependOutService(String componentName, String version,
                                                 JarModuleClassLoader jarModuleClassLoader) {
        List<ServiceInfo> services = jarModuleClassLoader.getServices();
        //如果没有依赖外部服务 直接返回
        if (CollUtil.isEmpty(services)) {
            return;
        }
        Object instance = jarModuleClassLoader.getComponentConsumer();
        if (ObjectUtil.isNull(instance)) {
            return;
        }
        Map<String, Object> consumerBeanMapping = ReflectUtil.invoke(instance,
                LambdaUtil.getMethodName(ComponentConsumer::getConsumerBeanMapping));


        for (ServiceInfo v : services) {
            String str = v.getComponentName();
            String serviceVersion = v.getVersion();
            String aClass = v.getClassPath();
            if (!str.equals(componentName) || !serviceVersion.equals(version)) {
                continue;
            }
            //
            Field field = getField(instance.getClass(), aClass);
            //卸载这个class
            consumerBeanMapping.remove(aClass);

            if (ObjectUtil.isNull(field)) {
                continue;
            }
            ReflectUtil.setFieldValue(instance, field, null);
        }
    }
}
